package com.eling.elcms.system;

import com.eling.elcms.core.AppContext;
import com.eling.elcms.system.model.RequestModel;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.persistence.Table;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;

/**
 * 请求监控的过滤器，将每个请求的客户IP地址，url，参数以及持续时间都记录下来，用于问题跟踪使用
 *
 * @author luoxd, KEN
 */
public class RequestMonitorFilter extends OncePerRequestFilter {
    private static final Logger log = LoggerFactory.getLogger(RequestMonitorFilter.class);

    private JdbcTemplate jdbcTemplate;

    private final String tableName = AnnotationUtils.findAnnotation(RequestModel.class, Table.class).name();
    private final String insertSql = "insert into " + tableName
            + " (id,userCode,url,param,accessTime,finished,remoteAddr,sessionID,userAgent) values (?,?,?,?,?,?,?,?,?)";
    private final String updateSql = "update " + tableName + " set finished = ? , responseDuration = ? where id= ? ";

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain fc)
            throws ServletException, IOException {
        RequestModel model = null;
        try {
            model = createModel(req);
        } catch (Exception e) {
            // 无论如何不能影响正常业务，吃掉异常，只记日志
            log.error(e.getMessage(), e);
        }

        fc.doFilter(req, resp);

        try {
            finishModel(model, req);
        } catch (Exception e) {
            // 无论如何不能影响正常业务，吃掉异常，只记日志
            log.error(e.getMessage(), e);
        }
    }

    private RequestModel createModel(HttpServletRequest req) {
        RequestModel m = new RequestModel();
        // pk值使用毫微秒数
        m.setId(System.nanoTime());
        m.setUserCode(AppContext.curUser().getCode());
        m.setRemoteAddr(AppContext.getClientInfo().getIp());
        m.setAccessTime(new Date());
        m.setUrl(req.getRequestURL().toString());
        if (StringUtils.isNotEmpty(req.getQueryString())) {
            m.setParam(req.getQueryString());
        } else {
            //修正post参数无法接收
            StringBuilder builder = new StringBuilder();
            Enumeration<String> params = req.getParameterNames();
            while (params.hasMoreElements()) {
                String paramName = params.nextElement();
                String paramValue = req.getParameter(paramName);
                builder.append(paramName).append("=").append(paramValue).append("&");
            }
            if (builder.length() > 0) {
                builder.deleteCharAt(builder.length() - 1);
            }
            m.setParam(builder.toString());
        }

        m.setFinished(false);
        m.setSessionID(req.getSession().getId());
        m.setUserAgent(req.getHeader("User-Agent"));

        Object[] args = new Object[]{m.getId(), m.getUserCode(), m.getUrl(), m.getParam(), m.getAccessTime(),
                m.getFinished(), m.getRemoteAddr(), m.getSessionID(), m.getUserAgent()};
        getJdbcTemplate(req).update(insertSql, args);

        return m;
    }

    private void finishModel(RequestModel model, HttpServletRequest req) {
        if (model != null) {
            long duration = System.currentTimeMillis() - model.getAccessTime().getTime();
            Object[] args = new Object[]{true, duration, model.getId()};
            getJdbcTemplate(req).update(updateSql, args);
        }
    }

    private JdbcTemplate getJdbcTemplate(HttpServletRequest req) {
        if (null == jdbcTemplate) {
            ServletContext application = req.getSession().getServletContext();
            ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(application);
            DataSource ds = (DataSource) ac.getBean("dataSource");
            jdbcTemplate = new JdbcTemplate(ds);
        }
        return jdbcTemplate;
    }

}
